#pragma once

#include <thread>
#include <memory>
#include <future>
#include <atomic>
#include <chrono>
#include <unordered_map>
#include <functional>

#include "define.h"
#include "mpmc_queue.h"
#include "utils.h"

// 线程等待时间 默认10s
static const int kThreadPoolWaitMills = 2000;

/**
 * 线程池
 */
class ThreadPool {
  // 线程池线程信息
  class ThreadInfo;

  // 任务类型
  using TaskType = std::function<void()>;
  // 线程信息
  using ThreadInfoMap = std::unordered_map<std::thread::id, ThreadInfo>;
  // 线程池任务队列
  using QueueType = MPMCQueue<TaskType>;

 public:
  /**
   * @param thread_n    线程池初始数量 默认以硬件为准
   * @param hold_n      线程池最小保持线程数 hold_n <= thread_n
   * @param max_n       线程池最大数量
   */
  ThreadPool(size_t thread_n = std::thread::hardware_concurrency(),
             size_t hold_n   = 1,
             size_t max_n    = std::thread::hardware_concurrency() * 2);

  virtual ~ThreadPool();

  /**
   * 主线程调用
   */
  void OnMainThreadTick();

  /**
   * 线程池开始工作
   */
  void Start();

  /**
   * 线程池停止工作
   */
  void Stop();

  /**
   * 提交任务 不需要返回任务执行结果
   */
  template <typename Func, typename... Args>
  void Submit(Func &&f, Args &&... args) {
    queue_.Enqueue(ForwardAsLambda(std::forward<decltype(f)>(f),
                                   std::forward<decltype(args)>(args)...));
  }

  /**
   * 提交任务 返回任务执行结果
   */
  template <typename Func, typename... Args>
  decltype(auto) SubmitWithRet(Func &&f, Args &&... args) {
    using ReturnType = std::invoke_result_t<Func, Args...>;

    auto task = std::make_shared<std::packaged_task<ReturnType()>>(
        ForwardAsLambda(std::forward<decltype(f)>(f),
                        std::forward<decltype(args)>(args)...));

    std::future<ReturnType> ret = task->get_future();
    queue_.Enqueue([task]() { (*task)(); });
    return ret;
  }

  /**
   * 供watcher 查看线程池状态
   */
  virtual std::string PrintState();

 protected:
  /**
   * 线程池开始时间点
   */
  virtual void OnStart();

  /**
   * 线程池结束时间点
   */
  virtual void OnStop();

  /**
   * 线程开始时间点
   */
  virtual void OnThreadStart();

  /**
   * 线程结束时间点
   */
  virtual void OnThreadEnd();

 private:
  /**
   * 线程工作循环
   */
  void WorkerLoop();

  /**
   * 处理下一个任务
   */
  bool ProcessNextTask();

  /**
   * 线程池移除空闲线程
   */
  void Slim();

  /**
   * 线程池增长
   */
  void Grow();

 private:
  /**
   * 线程信息
   */
  class ThreadInfo {
   public:
    ThreadInfo() = default;
    ThreadInfo(std::thread &&thread, int wait_mills)
        : handler_(std::make_unique<std::thread>(std::move(thread))),
          thread_id_(static_cast<size_t>(
              std::hash<std::thread::id>()(handler_->get_id()))),
          wait_mills_(wait_mills),
          last_busy_time_(std::chrono::steady_clock::now()),
          enabled_(true) {}
    ~ThreadInfo() {
      if (handler_ && handler_->joinable()) {
        handler_->join();
      }
      handler_.reset(nullptr);
    }

    ThreadInfo(const ThreadInfo &) = delete;
    ThreadInfo &operator=(const ThreadInfo &) = delete;

    ThreadInfo(ThreadInfo &&other) noexcept {
      if (this != &other) {
        this->handler_        = std::move(other.handler_);
        this->thread_id_      = other.thread_id_;
        this->task_counter_   = other.task_counter_;
        this->wait_mills_     = other.wait_mills_;
        this->last_busy_time_ = std::move(other.last_busy_time_);
      }
    }
    ThreadInfo &operator=(ThreadInfo &&) = delete;

    std::unique_ptr<std::thread> handler_{nullptr};  // 线程句柄
    size_t thread_id_{0};                            // 线程id hash后
    size_t task_counter_{0};                         // 任务计数器
    int wait_mills_{-1};  // 线程等待时间 <0 永久等待 单位毫秒
    std::chrono::steady_clock::time_point last_busy_time_;  // 上次活跃时间点
    bool enabled_{true};                                    // 可用标志
  };

  QueueType queue_;                   // 任务队列
  ThreadInfoMap live_map_;            // 活跃线程集合
  std::atomic<bool> shutdown_{true};  // 停止标志
  size_t current_thread_count_{0};    // 当前线程数量
  size_t hold_thread_count_{0};       // 最小保持线程数
  size_t max_thread_count_{0};        // 线程池最大线程数量
  std::mutex thread_pool_mutex_;      // 线程池数据锁
  std::mutex printer_mutex_;          // 线程池观察者锁
};